
from threading import Timer,Thread,Lock
import traceback
import string
import sys
import ma.log

from .athread import AThread



class TimerClass(Thread):
    """this class will run a timer for an interval passed to it and
    callback the class that initiated it as the timer expires  
    """

    def checkTimersDict(self):
        """This function will iterate over the dict of timers and decrement the
        timers and also call the respective callback functions whose timers have
        expired
        """
        
        try:
            #iterator declared over the dict of timers to traverse it
            #self.__log.debug("All timers updated")
            with self.__lock:
                if(len(self.timers_dict) != 0):
                    
                    list_of_expired_timers = []
                    #loop will iterate over dict and decrement the time passed from intervals
                    #of all timers since the last iteration
                    for i in self.timers_dict:
                        timer_data = self.timers_dict[i]
                        timer_data[1] = timer_data[1] - self.expire_interval 
                
                        #if the interval for a timer has expired the callback_func of timer will
                        #be called
                        if(timer_data[1] <= 0):
                            self.__log.debug("Timer expired - %s", str(timer_data))
                            list_of_expired_timers.append(i)
                            # reset timer to initial value
                            timer_data[1] = timer_data[0]
                            
                    #callback_func_thread = []
                    index = 0
                    while(index <len(list_of_expired_timers)):
                        #a thread is called to run the callback_func for expired timers
                        value = list_of_expired_timers[index]
                        timer_data = self.timers_dict[value]
                        th = AThread(timer_data[2], timer_data[3])
                        th.start()
                        index += 1
                        #for th in callback_func_thread:
                        #    th.join()
                        
        except Exception as err_msg:
            traceback.print_exception()
            self.__log.error("Error while checking timer dictionary: %s", err_msg)
            
        finally:
            self.startTimer()
    
    
    def startTimer(self):
        """ This function just starts the timer which on expiry prompts the
        checking of all the timers for expiry!
        """
        if not self.__to_kill:
            self.timer = Timer(self.expire_interval, self.checkTimersDict)
            self.timer.start()
        
        
    def run(self):
        """this function is called when the thread is started, it 
        reinitializes the timer for calling checking all the timers
        registered with this class
        """
        self.startTimer()
        
            
    def addTimer(self, interval, callback_func, arglist):
        """add a timer to the timers dict and increments the timer_id for
        the next timer to be assigned
        """
        with self.__lock:
            #append the timer's interval and callback_func
            list_timer_data = []
            list_timer_data.append(interval)
            
            #the value that will decrement over time
            list_timer_data.append(interval)
            list_timer_data.append(callback_func)
            list_timer_data.append(arglist)
            
            #insert the list in the dict at a new timer_id
            #note: empty list concat with the above list to make a new
            #list per a new timer
            
            self.timers_dict[self.timer_id] = list_timer_data + []
            self.timer_id +=1
            '''
            else:
                self.timers_dict_temp[self.timer_id] = list_timer_data + []
            
            '''
            #return the timer id to the function that initiates it
            return (self.timer_id - 1)
    
    
    def returnNextTimerId(self):
        """Returns the next time id that will be used when adding a timer.
        Useful when u want to pass the timer-id as an argument to the callback
        function
        """
        return self.timer_id
    
    
    def returnTimeToExpiry(self, timer_id):
        """Returns the range of the expiry time of a given timer after last 
        decrement, given its time_id. It returns two range values:
        returned_val_1 > [actual time to expiry] > returned_val_2
        """
        # check if timer_id is present in dictionary
        with self.__lock:
            if timer_id in self.timers_dict:
                return self.timers_dict[timer_id][1], self.timers_dict[timer_id][1] - self.expire_interval
            else:
                return None 
    
    
    def returnTimePassed(self, timer_id):
        """Returns the range of the time passed of a given timer after last 
        decrement, given its time_id. It returns two range values:
        returned_val_1 < [actual time passed] < returned_val_2
        """
        # check if timer_id is present in dictionary
        with self.__lock:
            if timer_id in self.timers_dict:
                time_passed = self.timers_dict[timer_id][0] - self.timers_dict[timer_id][1]
                return time_passed, time_passed + self.expire_interval
            else:
                return None 
    
    
    def removeTimer(self, timer_id):
        """This function will remove a timer from the timers dict and return the
        value items of this timer in a list [expiry_interval,
        time_interval_remaining, call_back_func]
        """
        with self.__lock:
            # check if timer_id is present in dictionary
            if timer_id in self.timers_dict:
                return self.timers_dict.pop(timer_id)
            else:
                return None
        
                    
    def resetTimer(self, timer_id):
        """This function will retrieve the timer data and reintroduce the user
        given interval into it hence effectively resetting the timer
        """
        with self.__lock:
            list_of_timer_data = self.timers_dict.pop(timer_id)
        
            #reassign the timout interval to the time_interval_remaining value to restart timer
            list_of_timer_data[1] = list_of_timer_data[0]
            self.timers_dict[timer_id] = [] + list_of_timer_data
             
        
    def killTimerThread(self):
        """This function is used to kill the timer thread, and the timer
        cannot be started again. This will stop sending all callbacks 
        from the added timers
        """
        with self.__lock:
            self.__to_kill = True
                            
                            
    def __init__(self, interval):
        #initialize __log for BroadcastListenServer
        
        self.__log = ma.log.get_logger("ma.utils")
        
        #ctor of superclass
        Thread.__init__(self)
               
        #expire interval for periodic checking on different timers 
        self.expire_interval = interval
        
        #an initial value for timer_id
        self.timer_id = 0
        
        #this is the timer that on expiry will trigger the periodic
        #checking of all timers for expiry
        self.timer = None
        
        #initializing the timers dictionary
        self.timers_dict = {} 
        
        #variable to stop the thread from running
        self.__to_kill = False
        
        # mlock for mutual exclusion
        self.__lock = Lock()
        
                
    
class Test:
    def func(self):
        print("the added timer expired")             
'''

def main(argv=None):
    mythread = TimerClass(5)
    print "hello"
    test = Test()
    mythread.addTimer(10,test.func)
    print 'hello'
    raw_input()
    
if __name__ == "__main__":
    """The program's entry point."""
    
    sys.exit(main())
'''        